<?php
// +----------------------------------------------------------------------
// | ZGUAN [ 致力于帮助更多的中小企业物联网转型 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2024 https://www.zguan.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ZGUAN 并不是自由软件，未经许可不能去掉ZGUAN 相关版权
// +----------------------------------------------------------------------
// | Author: ZGUAN Team <admin@zguan.cn>  0769-82282996  15899929162
// +----------------------------------------------------------------------
namespace app\common\repositories\energy;

use think\facade\Db;
use app\common\services\EnergyService;
use think\exception\ValidateException;
use app\common\dao\energy\EnergyMeterDao;
use app\common\repositories\BaseRepository;
use app\common\repositories\users\UsersRepository;
use app\common\repositories\bill\BillCateRepository;
use app\common\repositories\bill\BillFinanceRepository;
use app\common\repositories\property\users\propertyUsersRepository;
use app\common\repositories\energy\EnergySharedRoomRepository;
use app\common\repositories\energy\EnergyOperateLogRepository;
use app\common\repositories\property\users\PropertyUsersLogRepository;
use app\common\repositories\property\auth\PropertyAuthAccessRepository;

/**
 * Class EnergyMeterRepository
 *
 * @mixin EnergyMeterDao
 */
class EnergyMeterRepository extends BaseRepository
{
    const ENERGY_TYPE = [
        11 => '电表',
        12 => '冷水表',
        13 => '热水表',
        14 => '燃气表',
    ];

    public function __construct(EnergyMeterDao $dao)
    {
        $this->dao = $dao;
    }

    public function getPropertyList(array $where, $page, $limit, int $propertyId = null)
    {
        $query = $this->dao->search($where, $propertyId);
        $count = $query->count();
        $list = $query->page($page, $limit)
            ->with([
                'propertyInfo' => function ($query) {
                    $query->bind(['property_name' => 'name']);
                },
                'planInfo' => function ($query) {
                    $query->bind(['plan_name' => 'plan_name']);
                },
            ])
            ->order('id', 'desc')
            ->select();
        return compact('count', 'list');
    }

    /**
     * 接口获取水电列表
     * @param array $where
     * @param $page
     * @param $limit
     * @param int|null $propertyId
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     *
     * @author Amos
     * 2024-05-18 10:01
     */
    public function getApiList(array $where, $page, $limit, int $propertyId = null)
    {
        /** @var PropertyAuthAccessRepository $propertyAuthAccessRepository */
        $propertyAuthAccessRepository = app()->make(PropertyAuthAccessRepository::class);
        $propertyIds = $propertyAuthAccessRepository->getPropertyIdsByUserId($where['add_uid']);
        $query = $this->dao->search($where, $propertyId)
            ->where(function ($query) use ($where, $propertyIds) {
                ## 用户授权物业或用户自己添加电表
                $query->where('property_id', 'in', $propertyIds);
                $query->whereOr('add_uid', $where['add_uid']);
            });
        $count = $query->count();
        $list = $query->page($page, $limit)
            ->with([
                'roomInfo' => function ($query) {
                    $query->bind(['unit_room_name']);
                },
            ])
            ->field('id,room_id,is_public,is_bind,wit_type,unit_price,status,beilv,mesh_type,on_off,energy_name,energy_no,energy_type,read_time,read_z,read_j,read_f,read_p,read_g,expire_time')
            ->order('id', 'desc')
            ->select();
        return compact('count', 'list');
    }

    /**
     * 根据房间 ID 查询设备信息
     * @param array $where
     * @param $page
     * @param $limit
     * @param int|null $propertyId
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     *
     * @author Amos
     * 2024-06-13 16:15
     */
    public function getRoomIdList(array $where, $page, $limit, int $propertyId = null)
    {
        $query = $this->dao->search($where, $propertyId);
        $count = $query->count();
        $list = $query->page($page, $limit)
            ->with([
                'planInfo' => function ($query) {
                    $query->bind(['plan_name' => 'plan_name']);
                },
            ])
            ->field('id,wit_type,plan_type,plan_id,unit_price,status,beilv,on_off,energy_no,energy_type,read_time,read_z')
            ->order('id', 'desc')
            ->select();
        return compact('count', 'list');
    }


    public function addInfo(array $data, int $propertyId, int $userId = 0)
    {
        return Db::transaction(function () use ($data, $propertyId, $userId) {
            $data['add_uid'] = $userId;
            $data['property_id'] = $propertyId;
            $info = $this->dao->create($data);

            /** @var PropertyUsersLogRepository $repository */
            $repository = app()->make(PropertyUsersLogRepository::class);
            $repository->addInfo($propertyId, $userId, 'ENERGY.METER.ADD', $data);

            event('energy.meter.create', [
                'userId' => $userId,
                'meterInfo' => $info,
            ]);

            return $info;
        });
    }

    /**
     *  批量添加设备
     * @param array $data
     * @param int $propertyId
     * @param int $userId
     * @return void
     *
     * @author Amos
     * 2024-05-18 16:13
     */
    public function batchAdd(array $data, int $propertyId, int $userId)
    {
        $meterData = $data['meter_data'];
        $propertyId = $data['property_id'] ?? '0';
        $planType = $data['plan_type'] ?? '1';
        $witType = $data['wit_type'] ?? '1';
        foreach ($meterData as $k => $v) {
            $this->energyCheck($v['energy_no'], 0, $witType, $propertyId);
        }
        Db::startTrans();
        try {
            foreach ($meterData as $k => $v) {
                $meterInfoData = [
                    'add_uid' => $userId,
                    'wit_type' => $witType,
                    'plan_type' => $planType,
                    'property_id' => $propertyId,
                    'energy_no' => $v['energy_no'],
                    'energy_name' => $v['energy_name'] ?? '',
                    'beilv' => $v['beilv'] ?? 1,
                    'expire_time' => date('Y-m-t')
                ];
                $info = $this->create($meterInfoData);
                event('energy.meter.create', [
                    'userId' => $userId,
                    'meterInfo' => $info,
                ]);
            }
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            exception_log('添加设备失败', $e);
            throw new ValidateException('添加失败');
        }
    }

    /**
     * 扫码添加设备
     * @param array $data
     * @param int $propertyId
     * @param int $userId
     * @return mixed
     *
     * @author Amos
     * 2024-05-18 16:20
     */
    public function codeAdd(array $data, int $propertyId, int $userId = 0)
    {
        /** @var EnergyQrcodeRepository $energyQrcodeRepository */
        $energyQrcodeRepository = app()->make(EnergyQrcodeRepository::class);
        $codeInfo = $energyQrcodeRepository->getSearch([])
            ->where('energy_code', $data['energy_code'])
            ->where('is_bind', 2)
            ->find();
        if (!$codeInfo) {
            throw new ValidateException('二维码不存在或者己绑定');
        }
        if (!$codeInfo['energy_type']) {
            throw new ValidateException('二维码未绑定设备');
        }
        unset($data['energy_code']);
        return Db::transaction(function () use ($codeInfo, $data, $propertyId, $userId) {
            $data['wit_type'] = 1;
            $data['plan_type'] = 2;
            $data['is_public'] = 2;
            $data['is_bind'] = 2;
            $data['mesh_type'] = 2;
            $data['add_uid'] = $userId;
            $data['property_id'] = $propertyId;
            $data['beilv'] = $data['beilv'] ?? 1;
            $data['energy_no'] = $codeInfo['energy_no'];
            $data['energy_type'] = $codeInfo['energy_type'];
            $data['property_id'] = $data['property_id'] ?? '0';
            $info = $this->dao->create($data);

            $codeInfo->energy_id = $info['id'];
            $codeInfo->bind_time = date('Y-m-t H:i:s');
            $codeInfo->is_bind = 1;
            $codeInfo->save();

            /** @var PropertyUsersLogRepository $repository */
            $repository = app()->make(PropertyUsersLogRepository::class);
            $repository->addInfo($propertyId, $userId, 'ENERGY.METER.ADD', $data);

            event('energy.meter.create', [
                'userId' => $userId,
                'meterInfo' => $info,
            ]);
            return ['id' => $info['id']];
        });
    }


    public function editInfo($info, array $data, int $userId = 0)
    {
        return Db::transaction(function () use ($data, $info, $userId) {
            $res = $this->dao->update($info['id'], $data);
            /** @var PropertyUsersLogRepository $repository */
            $repository = app()->make(PropertyUsersLogRepository::class);
            $repository->addInfo($info['property_id'], $userId, 'ENERGY.METER.EDIT', $data);

            event('energy.meter.redact', [
                'meterInfo' => $info,
                'userId' => $userId,
            ]);

            return $res;
        });
    }

    public function getDetail(int $id)
    {
        $data = $this->dao->search([])
            ->where('id', $id)
            ->with([
                'planInfo' => function ($query) {
                    $query->bind(['plan_name' => 'plan_name', 'mode_type' => 'mode_type']);
                },
            ])
            ->find();
        return $data;
    }

    public function batchDelete(array $ids, int $userId = 0)
    {
        $list = $this->dao->selectWhere([
            ['id', 'in', $ids]
        ]);
        if ($list) {
            foreach ($list as $k => $v) {
                $this->deleteInfo($v, $userId);
            }
            return $list;
        }
        return [];
    }

    public function deleteInfo($info, int $userId = 0)
    {
        return Db::transaction(function () use ($info, $userId) {
            event('energy.meter.delete', [
                'userId' => $userId,
                'meterInfo' => $info,
            ]);

            /** @var PropertyUsersLogRepository $repository */
            $repository = app()->make(PropertyUsersLogRepository::class);
            $repository->addInfo($info['property_id'], $userId, 'ENERGY.METER.DEL', $info);
            $this->dao->delete($info['id']);
        });
    }


    /**
     *  结算时更新设备读数
     * @param $energyId
     * @param $updateData
     * @param string $remark
     * @return int
     * @throws \think\db\exception\DbException
     *
     * @author Amos
     * 2024-03-12 20:38
     */
    public function energyMeterUpdate($energyId, $updateData, string $remark = '')
    {
        return $this->dao->update($energyId, $updateData);
    }

    /**
     * 验证设备管理权限
     * @param int $id
     * @param $userId
     * @return bool
     * @throws \think\db\exception\DbException
     *
     * @author Amos
     * 2024-05-18 17:22
     */
    public function existsByUserId(int $id, $userId)
    {
        return $this->dao->search([])
                ->where(function ($query) use ($userId) {
                    /** @var PropertyAuthAccessRepository $propertyAuthAccessRepository */
                    $propertyAuthAccessRepository = app()->make(PropertyAuthAccessRepository::class);
                    $propertyIds = $propertyAuthAccessRepository->getPropertyIdsByUserId($userId);
                    ## 用户授权物业或用户自己添加电表
                    $query->where('property_id', 'in', $propertyIds);
                    $query->whereOr('add_uid', $userId);
                })->where('id', $id)->count() > 0;
    }


    /**
     * 绑定能源设备
     *
     * @param int $meterId
     * @param int $roomId
     * @return int
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function bindRoom(array $data, int $roomId, int $userId = 0)
    {
        return Db::transaction(function () use ($data, $roomId, $userId) {
            $meterInfo = $this->dao->get($data['meter_id']);
            if (!$meterInfo) {
                throw new ValidateException('设备不存在');
            }
            if ($meterInfo['property_id'] <= 0) {
                $this->dao->update($data['meter_id'], [
                    'is_bind' => 1,
                    'is_public' => 2,
                    'room_id' => $roomId,
                    'property_id' => $data['property_id']
                ]);
            } else {
                $this->dao->update($data['meter_id'], [
                    'is_bind' => 1,
                    'is_public' => 2,
                    'room_id' => $roomId
                ]);
            }

            /** @var PropertyUsersLogRepository $repository */
            $repository = app()->make(PropertyUsersLogRepository::class);
            $repository->addInfo($meterInfo['property_id'], $userId, 'ENERGY.METER.BINDROOM', $meterInfo);

            event('energy.meter.bindRoom', [
                'userId' => $userId,
                'roomId' => $roomId,
                'meterInfo' => $meterInfo,
                'remark' => $data['remark'] ?? '',
            ]);
            return $meterInfo;
        });
    }


    /**
     * 解绑能源设备
     * @param $meterInfo
     * @param $note
     */
    public function unbindByInfo($meterInfo, $remark, int $userId = 0)
    {
        return Db::transaction(function () use ($meterInfo, $remark, $userId) {

            $res = $this->dao->update($meterInfo['id'], ['room_id' => 0, 'is_bind' => 2]);

            event('energy.meter.unbindRoom', [
                'userId' => $userId,
                'roomId' => $meterInfo['room_id'],
                'meterInfo' => $meterInfo,
                'remark' => $remark ?? '',
            ]);
            return $res;
        });
    }


    /**
     * 人工抄表 更新数据
     * @param int $meterId 表ID
     * @param array $data
     * @param int|null $adminId
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function manuallyMeterReading($meterInfo, array $data, $userId = null)
    {
        Db::transaction(function () use ($meterInfo, $data, $userId) {
            ## 更新电表读数
            $this->dao->update($meterInfo['id'], [
                'read_z' => $data['read_z'],
                'read_j' => $data['read_j'],
                'read_f' => $data['read_f'],
                'read_p' => $data['read_p'],
                'read_g' => $data['read_g'],
                'read_time' => $data['day_time'],
            ]);
            event('energy.meter.handReading', [
                'userId' => $userId,
                'meterInfo' => $meterInfo,
            ]);
            ## 更新日统计表
            /** @var EnergyMeterDayRepository $meterDayRepository */
            $meterDayRepository = app()->make(EnergyMeterDayRepository::class);
            $meterDayRepository->addData($meterInfo, $data['read_z'], $data['read_j'], $data['read_f'], $data['read_p'], $data['read_g'], $data['day_time']);
        });
    }


    /**
     * 实时抄读
     * @param $meterInfo
     * @param $data
     * @param $userId
     * @return mixed
     *
     * @author Amos
     * 2024-04-10 11:29
     */
    public function autoReadCopy($meterInfo, $data, $userId = null)
    {
        if ($meterInfo['id'] <= 0) {
            return false;
        }
        return Db::transaction(function () use ($meterInfo, $data, $userId) {

            event('energy.meter.autoRead', [
                'userId' => $userId,
                'meterInfo' => $meterInfo,
                'remark' => $data['remark'],
            ]);

            /** @var EnergyOperateLogRepository $operateLogRepository */
            $operateLogRepository = app()->make(EnergyOperateLogRepository::class);
            $param['user_id'] = $userId;
            $param['remark'] = $data['remark'];
            $param['energy_id'] = $meterInfo['id'];
            $param['operate_type'] = $data['type'];
            $param['energy_type'] = $meterInfo['energy_type'];
            return $operateLogRepository->addInfo($meterInfo['property_id'], $param, $userId);
        });
    }


    /**
     * 状态更新
     * @param $meterInfo
     * @param $data
     * @param $userId
     * @return false|mixed
     *
     * @author Amos
     * 2024-04-10 15:17
     */
    public function updateOnOff($meterInfo, $data, $userId = null)
    {
        if ($meterInfo['id'] <= 0) {
            return false;
        }
        return Db::transaction(function () use ($meterInfo, $data, $userId) {
            event('energy.meter.updateOnOff', [
                'userId' => $userId,
                'meterInfo' => $meterInfo,
                'remark' => $data['remark'],
            ]);
            /** @var EnergyOperateLogRepository $operateLogRepository */
            $operateLogRepository = app()->make(EnergyOperateLogRepository::class);
            $param['user_id'] = $userId;
            $param['remark'] = $data['remark'];
            $param['energy_id'] = $meterInfo['id'];
            $param['operate_type'] = $data['type'];
            $param['energy_type'] = $meterInfo['energy_type'];
            return $operateLogRepository->addInfo($meterInfo['property_id'], $param, $userId);
        });
    }


    /**
     *  拉闸
     * @param $meterInfo
     * @param $data
     * @param $userId
     * @return false|mixed
     *
     * @author Amos
     * 2024-04-10 15:52
     */
    public function offEnergyMeter($meterInfo, $data, $userId = null)
    {
        if ($meterInfo['id'] <= 0) {
            return false;
        }
        return Db::transaction(function () use ($meterInfo, $data, $userId) {
            event('energy.meter.offEnergyMeter', [
                'userId' => $userId,
                'meterInfo' => $meterInfo,
                'remark' => $data['remark'],
            ]);
            /** @var EnergyOperateLogRepository $operateLogRepository */
            $operateLogRepository = app()->make(EnergyOperateLogRepository::class);
            $param['user_id'] = $userId;
            $param['remark'] = $data['remark'];
            $param['energy_id'] = $meterInfo['id'];
            $param['operate_type'] = $data['type'];
            $param['energy_type'] = $meterInfo['energy_type'];
            return $operateLogRepository->addInfo($meterInfo['property_id'], $param, $userId);
        });
    }

    /**
     * 合闸
     * @param $meterInfo
     * @param $data
     * @param $userId
     * @return false|mixed
     *
     * @author Amos
     * 2024-04-10 15:52
     */
    public function onEnergyMeter($meterInfo, $data, $userId = null)
    {
        if ($meterInfo['id'] <= 0) {
            return false;
        }
        return Db::transaction(function () use ($meterInfo, $data, $userId) {
            event('energy.meter.onEnergyMeter', [
                'userId' => $userId,
                'meterInfo' => $meterInfo,
                'remark' => $data['remark'],
            ]);
            /** @var EnergyOperateLogRepository $operateLogRepository */
            $operateLogRepository = app()->make(EnergyOperateLogRepository::class);
            $param['user_id'] = $userId;
            $param['remark'] = $data['remark'];
            $param['energy_id'] = $meterInfo['id'];
            $param['operate_type'] = $data['type'];
            $param['energy_type'] = $meterInfo['energy_type'];
            return $operateLogRepository->addInfo($meterInfo['property_id'], $param, $userId);
        });
    }


    /**
     *  设备编号检测
     * @param string $deviceNo
     * @param int|null $id
     * @param int $isSmartMeter
     * @param int $propertyId
     * @return mixed|void
     *
     * @author Amos
     * 2024-04-14 20:32
     */
    public function energyCheck(string $energyNo, int $id = null, int $isSmartMeter = 1, int $propertyId = 0)
    {
        if ($this->dao->energyExists($energyNo, $id)) {
            throw new ValidateException($energyNo . '设备号已存在，请勿重复添加');
        }
        if ($isSmartMeter == 1) {
            $server = EnergyService::init($propertyId)->Electric();
            $res = $server->queryElectricMeterDetail($energyNo)->execute();
            if ($res['code'] !== 200) {
                throw new ValidateException($res['msg']);
            }
            return $res['data'];
        }
    }

    /**
     * 系统自动抄表 更新数据
     * @param int $meterId
     * @param array $data
     * @param $time
     * @return mixed
     *
     * @author Amos
     * 2024-05-08 18:05
     */
    public function taskMeterReadingHandle(int $meterId, array $data, $time = '')
    {
        return Db::transaction(function () use ($meterId, $data, $time) {
            $meterInfo = $this->dao->get($meterId);
            if (!$meterInfo) {
                return false;
            }
            $readNumZ = $data['read_z'] ?? 0;
            $readNumJ = $data['read_j'] ?? 0;
            $readNumF = $data['read_f'] ?? 0;
            $readNumP = $data['read_p'] ?? 0;
            $readNumG = $data['read_g'] ?? 0;

            ## 更新电表最新读数
            $this->dao->update($meterId, [
                'status' => 1,
                'read_z' => $readNumZ,
                'read_j' => $readNumJ,
                'read_f' => $readNumF,
                'read_p' => $readNumP,
                'read_g' => $readNumG,
                'read_time' => date('Y-m-d H:i:s')
            ]);

            ## 添加 日用量记录
            /** @var EnergyMeterDayRepository $meterDayRepository */
            $meterDayRepository = app()->make(EnergyMeterDayRepository::class);
            $meterDayRepository->addData($meterInfo, $readNumZ, $readNumJ, $readNumF, $readNumP, $readNumG, $time);
            if ($meterInfo['is_public'] == 1 && $meterInfo['is_bind'] == 1 && $readNumZ > $meterInfo['read_z']) {
                ## 公摊表 且绑定公把摊方案
                /** @var EnergySharedRoomRepository $energySharedRoomRepository */
                $energySharedRoomRepository = app()->make(EnergySharedRoomRepository::class);
                $energySharedRoomRepository->usageAllocation($meterInfo['id'], '日用量分配');
            }
            if ($meterInfo['plan_type'] == 2) {
                ## 系统预付费结算
                $this->prepaidEquipmentDeduction($meterInfo['id']);
            }
        });
    }

    /**
     * 预付设备扣费
     * @param int $deviceType
     * @param int $deviceId
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function prepaidEquipmentDeduction(int $meterId)
    {
        $meterInfo = $this->dao->get($meterId);
        if (!$meterInfo) {
            return false;
        }
        // 预付表才进行操作
        if ($meterInfo && $meterInfo['plan_type'] == 2) {
            $planInfo = $meterInfo['planInfo'] ?? []; ## 计费方案
            if ($planInfo) {
                $energyBillData = [
                    'start_num_z' => $meterInfo['base_z'],
                    'start_num_j' => $meterInfo['base_j'],
                    'start_num_f' => $meterInfo['base_f'],
                    'start_num_p' => $meterInfo['base_p'],
                    'start_num_g' => $meterInfo['base_g'],
                    'end_num_z' => $meterInfo['read_z'],
                    'end_num_j' => $meterInfo['read_j'],
                    'end_num_f' => $meterInfo['read_f'],
                    'use_num_p' => $meterInfo['read_p'],
                    'use_num_g' => $meterInfo['read_g'],
                ];
                ## 更新结算数据
                $this->dao->update($meterInfo['id'], [
                    'base_z' => $meterInfo['read_z'],
                    'base_j' => $meterInfo['read_j'],
                    'base_f' => $meterInfo['read_f'],
                    'base_p' => $meterInfo['read_p'],
                    'base_g' => $meterInfo['read_g'],
                    'base_date' => date("Y-m-d")
                ]);

                ##  创建能源账单
                /** @var EnergyBillRepository $energyBillRepository */
                $energyBillRepository = app()->make(EnergyBillRepository::class);

                $energyBillData = $energyBillRepository->summaryBillSettlement($energyBillData, $planInfo);
                if ($energyBillData['total_amount'] > 0) {
                    $energyBillData['month_time'] = date('Y-m');

                    $energyBillData['plan_type'] = 2; ## 系统预付费账单
                    $energyBillData['start_date'] = $meterInfo['base_date'];
                    $energyBillData['end_date'] = date("Y-m-d");
                    $energyBillData['energy_id'] = $meterInfo['id'];
                    $energyBillData['room_id'] = $meterInfo['room_id'] ?? '';
                    $energyBillData['energy_name'] = $meterInfo['energy_name'] ?? '';
                    $energyBillData['energy_type'] = $meterInfo['energy_type'] ?? '';
                    $energyBillData['property_id'] = $meterInfo['property_id'] ?? '';
                    $energyBillData['plan_record'] = json_encode($planInfo, JSON_UNESCAPED_UNICODE);
                    $energyBillInfo = $energyBillRepository->create($energyBillData);
                    if ($energyBillInfo) {
                        ## 生成扣款流水记录
                        $this->balanceChange($meterInfo['id'], 2, $energyBillData['total_amount'], 2, '预付费结算' . $energyBillData['remark']);
                    }
                }
            } else if ($meterInfo['plan_id'] <= 0 && $meterInfo['unit_price'] > 0) {
                $beilv = $meterInfo['beilv'] ?? 1;
                $energyBillData['use_num_z'] = bcmul(bcsub($meterInfo['read_z'], $meterInfo['base_z'], 4), $beilv, 4); # 总用量
                $energyBillData['remark'] = '用量' . $energyBillData['use_num_z'] . '度'; ## 备注说明
                $energyBillData['total_amount'] = bcmul($meterInfo['unit_price'], $energyBillData['use_num_z'], 4); ## 扣款总金额
                ## 生成扣款流水记录
                $this->balanceChange($meterInfo['id'], 2, $energyBillData['total_amount'], 2, '预付费结算' . $energyBillData['remark']);
            }
        }
    }


    /**
     *  设备充值扣款
     * @param int $meterId 设备 ID
     * @param int $isType 变动方式 1 增加 2 减少
     * @param float $amount 变动金额
     * @param int $logType 变动类型 1 充值 2 扣款 3 退款
     * @param string $remark 备注说明
     * @param int $userId 用户 ID
     * @return false|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     *
     * @author Amos
     * 2024-05-09 22:01
     */
    public function balanceChange(int $meterId, $isType, $amount, $logType, $remark, int $userId = 0)
    {
        $meterInfo = $this->dao->get($meterId);
        if (!$meterId) {
            return false;
        }
        if ($isType == 1) {
            ## 增加金额
            $afterChangeAmount = bcadd($meterInfo['balance'], $amount, 2);
            $this->dao->incField($meterInfo['id'], 'balance', (float)$amount);
        }
        if ($isType == 2) {
            ## 减少金额
            $afterChangeAmount = bcsub($meterInfo['balance'], $amount, 2);
            $this->dao->incField($meterInfo['id'], 'balance', (float)$amount);
        }
        $this->dao->update($meterInfo['id'], [
            'balance' => $afterChangeAmount,
        ]);

        $balanceData['amount'] = $amount;
        $balanceData['user_id'] = $userId;
        $balanceData['log_type'] = $logType;
        $balanceData['energy_id'] = $meterInfo['id'];
        $balanceData['room_id'] = $meterInfo['room_id'] ?? '0';
        $balanceData['before_change'] = $meterInfo['balance'];
        $balanceData['property_id'] = $meterInfo['property_id'] ?? '0';
        $balanceData['contract_id'] = $meterInfo['contract_id'] ?? '0';
        ## 变动后金额
        $balanceData['after_change'] = $afterChangeAmount;
        $balanceData['remark'] = $remark;

        /** @var EnergyBalanceLogRepository $energyBalanceLogRepository */
        $energyBalanceLogRepository = app()->make(EnergyBalanceLogRepository::class);
        $energyBalanceLogRepository->create($balanceData);

        if ($isType == 2) {
            ## 能源设备预付费扣费消息
            \app\jobs\Queue::push(\app\jobs\SendMessageJob::class, [
                'message_type' => 'energy_fee_deduction',
                'property_id' => $meterInfo['property_id'],
                'params' => [
                    'fee_deduction' => $amount,
                    'energy_id' => $meterInfo['id'],
                    'after_change' => $afterChangeAmount,
                    'energy_no' => $meterInfo['energy_no'],
                    'energy_name' => $meterInfo['energy_name'],
                    'property_id' => $meterInfo['property_id'],
                ]
            ]);
        }
        if ($isType == 1) {
            ## 发送充值消息
            \app\jobs\Queue::push(\app\jobs\SendMessageJob::class, [
                'message_type' => 'energy_balance_recharge',
                'property_id' => $meterInfo['property_id'],
                'params' => [
                    'recharge_amount' => $amount,
                    'energy_id' => $meterInfo['id'],
                    'after_change' => $afterChangeAmount,
                    'energy_no' => $meterInfo['energy_no'],
                    'energy_name' => $meterInfo['energy_name'],
                ]
            ]);
        }

        ## 余额不足断电
        if ($afterChangeAmount <= 0 && $amount < 0 && $meterInfo && $meterInfo['off_type'] == 2) {
            ## 设备断电通知
            \app\jobs\Queue::push(\app\jobs\SendMessageJob::class, [
                'message_type' => 'energy_off',
                'property_id' => $meterInfo['property_id'],
                'params' => [
                    'energy_id' => $meterInfo['id'],
                    'after_change' => $afterChangeAmount,
                    'energy_no' => $meterInfo['energy_no'],
                    'energy_name' => $meterInfo['energy_name'],
                ]
            ]);
            $offEnergyMeterDaa['type'] = 'off';
            $offEnergyMeterDaa['id'] = $meterInfo['id'];
            $offEnergyMeterDaa['remark'] = '设备余额不足';
            $this->offEnergyMeter($meterInfo, $offEnergyMeterDaa);
        }
        ## 充值后通电
        if ($afterChangeAmount >= 0 && $amount > 0 && $meterInfo) {
            if ($meterInfo['on_off'] == 2) {
                ## 断开状态
                $onEnergyMeterDaa['type'] = 'on';
                $onEnergyMeterDaa['id'] = $meterInfo['id'];
                $onEnergyMeterDaa['remark'] = '充值成功';
                ## 充值成功后 送水送电
                $this->onEnergyMeter($meterInfo, $onEnergyMeterDaa);

            } elseif ($meterInfo['on_off'] == 1) {
                ##  通畅状态
                if (in_array($meterInfo['energy_type'], [12, 13]) && ($meterInfo['mesh_type'] ?? 0) == 3) {
                    ##  NB通讯 有停水停电未执行成功的，取消任务
                    /** @var EnergyOperateLogRepository $energyOperateLogRepository */
                    $energyOperateLogRepository = app()->make(EnergyOperateLogRepository::class);
                    $count = $energyOperateLogRepository->getSearch([])
                        ->where('energy_id', $meterInfo['id'])
                        ->where('operate_type', 'off')
                        ->where('status', 1)
                        ->count();
                    if ($count > 0) {
                        ## 取消未执行的停止任务
                        $energyOperateLogRepository->cancelMeterId($meterInfo['id']);
                    }
                }
            }

        }
        return true;
    }


    /**
     * 预付设备 充值
     * @param int $meterId 设备 ID
     * @param $data
     * @param int $userId 用户 ID
     * @return false|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     *
     * @author Amos
     * 2024-05-09 22:41
     */
    public function rechargeInfo(int $meterId, $data, int $userId = 0)
    {
        $meterInfo = $this->dao->get($meterId);
        if (!$meterInfo) {
            return false;
        }

        return Db::transaction(function () use ($meterInfo, $data, $userId) {

            /** @var PropertyUsersLogRepository $repository */
            $repository = app()->make(PropertyUsersLogRepository::class);
            $repository->addInfo($meterInfo['property_id'], $userId, 'ENERGY.METER.RECHARGE', $meterInfo);

            if ($data['pay_amount'] > 0) {
                ## 生成充值流水记录
                if ($data['is_type'] == 2) {
                    $logType = 3;
                    $remark = $data['remark'] . ' 退款';
                } else {
                    $logType = 1;
                    $remark = $data['remark'] . ' 充值';
                }
                $this->balanceChange($meterInfo['id'], $data['is_type'], $data['pay_amount'], $logType, $remark, $userId);

                ## 生成收支流水明细
                /** @var BillFinanceRepository $billFinanceRepository */
                $billFinanceRepository = app()->make(BillFinanceRepository::class);
                $billFinanceRepository->rechargeAdd($meterInfo, $data, $userId);
            }
            return true;
        });

    }


    /**
     * 切换收费类型
     * @param $meterInfo
     * @param $param
     * @param $userId
     */
    public function switchPlanType($meterInfo, $param, int $userId = 0)
    {
        Db::startTrans();
        try {
            ## 修改设备收费类型
            $this->dao->update($meterInfo['id'], [
                'plan_type' => $param['plan_type']
            ]);
            ## 设备充值
            if (($param['plan_type'] == 2 || $param['plan_type'] == 3) && $param['balance'] > 0) {
                $data['is_type'] = 1;
                $data['pay_type'] = 2;
                $data['pay_method'] = 4;
                $data['pay_amount'] = $param['balance'];
                ## 生成充值流水记录
                $this->balanceChange($meterInfo['id'], $data['is_type'], $data['pay_amount'], 1, '切换收费类型充值', $userId);

                ## 生成收支流水明细
                /** @var BillFinanceRepository $billFinanceRepository */
                $billFinanceRepository = app()->make(BillFinanceRepository::class);
                $billFinanceRepository->rechargeAdd($meterInfo, $data, $userId);
            }
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            exception_log('设备收费类型切换失败', $e);
            throw new ValidateException('收费类型切换失败');
        }
    }


    /**
     * 修改设备倍率
     * @param $companyUserInfo
     * @param $meterInfo
     * @param $param
     * @param $trackType
     * @return void
     *
     * @author Amos
     * 2024-05-18 22:42
     */
    public function modifyBeilv($meterInfo, $param, int $userId = 0)
    {
        Db::startTrans();
        try {
            ## 修改电表倍率
            $this->dao->update($meterInfo['id'], [
                'beilv' => $param['beilv']
            ]);

            event('energy.meter.modifyBeilv', [
                'userId' => $userId,
                'meterInfo' => $meterInfo,
                'remark' => $param['remark'] . '，' . $meterInfo['beilv'] . ' 倍更新至' . $param['beilv'] . ' 倍',
            ]);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            exception_log('电表修改倍率失败', $e);
            throw new ValidateException('电表倍率修改失败');
        }
    }

    /**
     *  预付费 收费方案更新
     * @param $meterId
     * @param $param
     * @param int $userId
     * @return int
     * @throws \think\db\exception\DbException
     *
     * @author Amos
     * 2024-06-07 17:14
     */
    public function switchPlanPrice($meterId, $param, int $userId = 0)
    {
        return $this->dao->update($meterId, [
            'plan_id' => $param['plan_id'],
            'unit_price' => $param['unit_price']
        ]);
    }


    /**
     *  设备权益转让
     * @param $meterInfo
     * @param $param
     * @param int $userId
     * @return void
     *
     * @author Amos
     * 2024-05-18 23:00
     */
    public function makeOverInfo($meterInfo, $param, int $userId = 0)
    {
        /** @var UsersRepository $usersRepository */
        $usersRepository = app()->make(UsersRepository::class);
        $userInfo = $usersRepository->getInfoByMobile($param['mobile']);
        if ($userInfo['id'] <= 0) {
            throw new ValidateException($param['mobile'] . '用户不存在');
        }

        /** @var PropertyUsersRepository $propertyUsersRepository */
        $propertyUsersRepository = app()->make(PropertyUsersRepository::class);
        $propertyUsersInfo = $propertyUsersRepository->getInfoByUserId($userInfo['id']);
        if ($propertyUsersInfo['id'] <= 0) {
            throw new ValidateException($param['mobile'] . '用户非管理员，禁止转让');
        }
        return Db::transaction(function () use ($meterInfo, $userInfo, $param, $userId) {
            ## 修改设备权益
            $this->dao->update($meterInfo['id'], [
                'add_uid' => $userInfo['id']
            ]);
            event('energy.meter.makeOver', [
                'userId' => $userId,
                'meterInfo' => $meterInfo,
                'remark' => $param['remark'] . '，' . ' 设备权益转让至' . $userInfo['username'],
            ]);

        });

    }

    /**
     * 根据条件查询设备
     * @param int|null $propertyId
     * @param array $where
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     *
     * @author Amos
     * 2024-05-09 18:46
     */
    public function getMeterData(int $propertyId = null, array $where = [])
    {
        $list = $this->dao->search($where, $propertyId)
            ->field('energy_type,energy_no,energy_name,id')
            ->select()->toArray();
        return $list;
    }
}